using System;
using System.Collections.Generic;
using System.Text;
 
namespace Dsp
{
    /// <summary> complex number in double precision </summary>
    public struct Complex : IEquatable<Complex>, IComparable<Complex>
    {
        double re;
        double im;
        double abs;
        double arg;

        // Is polar form in-sync with rectangular form?
        bool sync; 

        #region constructors
        public Complex(double re, double im)
        {
            this.re  = re;
            this.im  = im;
            this.abs = 0.0;
            this.arg = 0.0;

            sync = false;
        }

        public Complex(double re)
        {
            this.re  = re;
            this.im  = 0.0;
            this.abs = 0.0;
            this.arg = 0.0;

            sync = false;
        }

        static public Complex Polar(double abs, double arg)
        { return new Complex(abs * Math.Cos(arg), abs * Math.Sin(arg)); }      
        #endregion

        #region Private members
        private void ComputePolar()
        {            
            abs = Math.Sqrt(re*re + im*im);
            arg = Math.Atan(im/re);
            sync = true;
        }

        private void ComputeRect()
        {
            re = abs * Math.Cos(arg);
            im = abs * Math.Sin(arg);
        }
        #endregion

        #region Properties
        static public readonly Complex Zero = new Complex(0.0, 0.0);
        static public readonly Complex One  = new Complex(1.0, 0.0);
        static public readonly Complex I    = new Complex(0.0, 1.0);

        public double Re
        {
            get { return re;  }
            set { re = value; }
        }
        
        public double Im
        {
            get { return im;  }
            set { im = value; }
        }

        public double Abs
        {
            get
            {
                if (!sync)
                    ComputePolar();
                return abs;
            }
            set
            {
                abs = value;
                ComputeRect();
            }
        }

        public double Arg
        {
            get
            {
                if (!sync)
                    ComputePolar();
                return arg;
            }
            set
            {
                arg = value;
                ComputeRect();
            }
        }

        #endregion

        #region ToString
        public override string ToString()
        {
            return re.ToString() + " + " + im.ToString() + "i";
        }

        public string ToString(string format)
        { // TODO
            return re.ToString() + " + " + im.ToString() + "i";
        }
        #endregion

        #region Unary operators 
        static public Complex operator +(Complex a)
        { return new Complex(a.re, a.im); }

        static public Complex operator -(Complex a)
        { return new Complex(-a.re, -a.im); }

        static public Complex operator ~(Complex a)
        { return new Complex(a.re, -a.im); }

        #endregion

        #region Add
        static public Complex operator +(Complex a, Complex b)
        { return new Complex(a.re + b.re, a.im + b.im); }

        static public Complex operator +(double a, Complex b)
        { return new Complex(b.re + a, b.im); }

        static public Complex operator +(Complex a, double b)
        { return new Complex(a.re + b, a.im); }

        #endregion

        #region Sub
        static public Complex operator -(Complex a, Complex b)
        { return new Complex(a.re - b.re, a.im - b.im); }

        static public Complex operator -(double a, Complex b)
        { return new Complex(b.re - a, b.im); }

        static public Complex operator -(Complex a, double b)
        { return new Complex(a.re - b, a.im); }

        #endregion

        #region Product
        static public Complex operator *(Complex a, Complex b)
        { return new Complex(a.re * b.im, a.re - b.im); }

        static public Complex operator *(double a, Complex b)
        { return new Complex(b.re * a, b.im * a); }

        static public Complex operator *(Complex a, double b)
        { return new Complex(a.re * b, a.im * b); }
        #endregion

        #region Division
        static public Complex operator /(Complex a, Complex b)
        {   
            double mp2= b.re*b.re + b.im*b.im;
            return new Complex((a.re * b.re + a.im * b.im) / mp2, (a.im * b.re - a.re * b.im) / mp2);
        }

        static public Complex operator /(double a, Complex b)
        { return new Complex(b.re / a, b.im / a); }

        static public Complex operator /(Complex a, double b)
        { return new Complex(a.re / b, a.im / b); }


        #endregion

        #region Equal operators
        static public bool operator ==(Complex a, Complex b)
        { return (a.re == b.re) && (a.im == b.im); }

        static public bool operator !=(Complex a, Complex b)
        { return (a.re != b.re) || (a.im != b.im); }
        #endregion

        #region Interface IEquatable and Hashing        
        public override bool Equals(object obj)
        { return (obj is Complex) && this.Equals((Complex) obj); }

        public bool Equals(Complex obj)
        { return (this.re == obj.re) && (this.im == obj.im); }

        
        public override int GetHashCode()
        { return re.GetHashCode() ^ im.GetHashCode(); }
        #endregion

        #region Interface IComparable

        public int CompareTo(Complex other)
        {
            if (!sync)
                ComputePolar();

            if (other.abs > this.abs) return +1;
            if (other.abs < this.abs) return -1;
            return 0;
        }

        #endregion
        
    }
}
